在博客 Android 插件化 – 类的动态加载实践 中我们通过一个实例来实践了类的动态加载以及使用。接下来从源码角度来分析一下类的动态加载是如何实现的。
流程图
1 | ├── BaseDexClassLoader |
构造方法
先来看一下 BaseDexClassLoader 的构造方法:
1 | public BaseDexClassLoader(String dexPath, File optimizedDirectory, |
这里除了调用父类 ClassLoader 的构造方法之外,就是创建了 DexPathList 对象。
1 | private ClassLoader(Void unused, ClassLoader parent) { |
ClassLoader 的构造方法很简单,就是把参数中的父 ClassLoader 赋值给成员变量。
DexPathList
下面来看一下 DexPathList 构造方法:
1 | public DexPathList(ClassLoader definingContext, String dexPath, |
下面来看一下 DexPathList.makeElements 代码:
1 | private static Element[] makeElements(List<File> files, File optimizedDirectory, |
ClassLoader 构造方法部分我们分析完了,其实整个过程就是调用了 DexPathList 的构造函数,把 dex 文件或者 Native libraries 加载到 Element 数组的过程。
体现在 DexPathList 中的 dexElements 和 nativeLibraryPathElements 成员变量,供 DexPathList.findClass 和 DexPathList.findLibrary 方法查询。
上面的方法中调用了 DexPathList.loadDexFile 方法:
1 | private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, |
这里可以看到,针对是否指定 optimizedDirectory 做了不同的处理。这里其实是分别调用了不同的 DexFile 构造方法。
DexFile
1 | private static Object openDexFile(String sourceName, String outputName, int flags, |
DexFile 主要就是调用了 native 方法 openDexFileNative 来打开 dex 文件。
加载类
下面来看一下类的加载过程,这里我们要关注一下 ClassLoader 的双亲委托机制:
1 | protected Class<?> loadClass(String name, boolean resolve) |
findClass 方法其实调用的是 BaseDexClassLoader 的 findClass 方法:
1 | @Override |
其实调用的是 DexPathList.findClass 方法:
1 | public Class findClass(String name, List<Throwable> suppressed) { |
类的加载重点在 DexFile.defineClass 方法,这里面会调用 native 方法 defineClassNative。这里面的代码暂时不做分析。
总结
从上面分析我们可以看出,在构造 DexClassLoader 和 PathClassLoader 时,会加载 jar/apk/zip/dex 中的 dex 文件以及 Native libraries 并保存到 Element 数组中。后面动态加载类时都是从保存到数组中的 dex 文件中搜索,并调用 DexFile 的 Native 方法进行加载的过程。Native 方法这里暂不分析,有兴趣的大家可以自行研究。